/******************************************************
* Team WB
* Brett Cate, William Kim
* 
* Main Program File
* This program handles the user interface for the SC-2
* computer simulation using the ncurses library.
*******************************************************/

#include "sc2_main.h"

/*
Main function to run the simulator.
*/
int main() {
	int input = 0;
	int cpuError, debugError, ch;
	char fileName[20];
	WINDOW* welcome;

	// initialize sc2 machine ADTs
	CPUPtr my_cpu = cpu_new(&cpuError);
	RegisterPtr swPtr = my_cpu->SW;
	RegisterFilePtr my_registers = my_cpu->registerFile;
	MemoryModulePtr my_memory = my_cpu->mainMemory;
	ALUPtr my_alu = my_cpu->alu;
	DebugMonitorPtr my_debugger = debugMonitor_new(my_cpu, &debugError);
	register_putRegValue(swPtr, 0x0000);

	initscr();
	cbreak();
	refresh();

	
	welcome = newwin(7,36,1,4);
	wborder(welcome,'*','*','*','*','*','*','*','*');
	mvwaddstr(welcome,2,2,"Welcome to the WB SC-2 Simulator");
	mvwaddstr(welcome,3,2,"Programmers: Brett Cate");
	mvwaddstr(welcome,4,15,"William Kim");
	wrefresh(welcome);

	while (ch != '9') {
		ch = mainMenu();
		switch (ch) {
			case '1' :
				printw("Please enter the file name of the program to load (including extension)\n");
				fflush(stdout);
				scanw("%s",fileName);
				printw("\n");
				debugMonitor_loadProgram(my_debugger,fileName);
				printw("Program loaded\n");
				break;
			case '2' :
				main_registerMenu(my_registers);
				break;
			case '3' :
				main_memoryMenu(my_memory);
				break;
			case '4' :
				debugMonitor_addBreakpoint(my_debugger);
				break;
			case '5' :
				erase();
				debugMonitor_runProgram(my_debugger);
				cpu_run(my_cpu,(struct DebugMonitor*)my_debugger);
				debugMonitor_showAll(my_debugger);
				erase();
				break;
			case '6' :
				debugMonitor_stepProgram(my_debugger);
				debugMonitor_showAll(my_debugger);
				cpu_run(my_cpu,(struct DebugMonitor*)my_debugger);
				debugMonitor_showAll(my_debugger);
				erase();
				break;
			case '7' :
				debugMonitor_showAll(my_debugger);
				getch();
				erase();
				break;
			case '8' :
				my_cpu = cpu_new(&cpuError);
				swPtr = my_cpu->SW;
				my_registers = my_cpu->registerFile;
				my_memory = my_cpu->mainMemory;
				my_alu = my_cpu->alu;
				my_debugger = debugMonitor_new(my_cpu, &debugError);
				register_putRegValue(swPtr, 0x0000);
				break;
			default :
				break;
		}
	}

	endwin();

	return 0;
}

/*
Returns a welcome window for this program.
*/
int mainMenu() {

	
	int mainMenu_input = 0;
	mvprintw(10,0,"Please choose an option: \n");
	printw("1. Load Program\n");
	printw("2. Manage Registers\n");
	printw("3. Manage Memory\n");
	printw("4. Set Breakpoints\n");
	printw("5. Execute Program\n");
	printw("6. Execute Program (in STEP mode)\n");
	printw("7. Show All\n");
	printw("8. Reinitilize Machine (perform between executions)\n");
	printw("9. Quit\n");
			
	refresh();

	mainMenu_input = getch();
	printw("\n");
	
	return mainMenu_input;
}


/* 
Displays register menu options and prompts for user choice.
*/
void main_registerMenu(RegisterFilePtr the_registerFilePtr) {
	int registerMenu_input = 0;
	int registerMenu_index = 0;
	int which_byte = 0;
	int registerMenu_error = NO_ERROR;
	int registerNumber, registerValue;
	erase();
	while (registerMenu_input != '6') {
		registerMenu_error = NO_ERROR;
		mvprintw(10,0,"Please choose an option: \n");
		printw("1. Display Registers\n");
		printw("2. Get Byte From...\n");
		printw("3. Put Byte To...\n");
		printw("4. Get Register Value...\n");
		printw("5. Put Register Value...\n");
		printw("6. Back\n");		
			
		registerMenu_input = getch();
		printw("\n");

		switch (registerMenu_input) {
			
			case '1' :
				printw("Register File\n");
				for (registerMenu_index = 0; registerMenu_index < 16; registerMenu_index++) {
					printw("$R%-3X :  %04X\n", registerMenu_index, *the_registerFilePtr[registerMenu_index]);
				}
				printw("\n");
				break;
			case '2' :
				printw("Enter a register number and which byte you want (1 : HOB; 0 : LOB): \n");
				scanw("%X %d", &registerNumber, &which_byte);
				printw("\n");
				printw("Value : %02X\n\n", registerFile_getByteFrom(the_registerFilePtr, registerNumber, which_byte, &registerMenu_error)); 
				break;
			case '3' :
				printw("Enter a register number, which byte (1 : HOB; 0 : LOB), and value : ");
				scanw("%X %d %X", &registerNumber, &which_byte, &registerValue);
				printw("\n");
				registerMenu_error = registerFile_putByteTo(the_registerFilePtr, registerNumber, which_byte, registerValue); 
				break;
			case '4' :
				printw("Enter a register number : ");
				scanw("%X", &registerNumber);
				printw("\n");
				printw("Value : %04X\n\n", registerFile_getRegValue(the_registerFilePtr, registerNumber, &registerMenu_error)); 
				break;
			case '5' :
				printw("Enter a register number and a value to put : ");
				scanw("%X %X", &registerNumber, &registerValue);
				printw("\n");
				registerMenu_error = registerFile_putRegValue(the_registerFilePtr, registerNumber, registerValue); 
				break;
			case '6' :
				erase();
				break;
			default :
				break;
		}
		if(registerMenu_error)
			printw("Register Error! : %s \n\n", main_errorMessage(registerMenu_error)); 
	}
}
/* 
Displays memory menu options and prompts for user choice.
*/
void main_memoryMenu(MemoryModulePtr the_memoryModulePtr) {
	int memoryMenu_input = 0;
	int memoryMenu_index = 0;
	int memoryMenu_error = NO_ERROR;
	int memAddr, memAddr2, memVal;
	erase();
	while (memoryMenu_input != '6') {
		memoryMenu_error = NO_ERROR;
		mvprintw(10,0,"Please choose an option: \n");
		printw("1. Display Memory in Range...\n");
		printw("2. Fetch Byte From...\n");
		printw("3. Store Byte To...\n");
		printw("4. Fetch Word From...\n");
		printw("5. Store Word To...\n");
		printw("6. Back\n");		
			
		memoryMenu_input = getch();
		printw("\n");

		switch (memoryMenu_input) {
			
			case '1' :
				printw("Enter a beginning and ending memory address : \n");
				scanw("%X %X", &memAddr, &memAddr2);
				printw(      "Memory\n");
				printw(      "Address  Hi Lo\n");
				for (memoryMenu_index = memAddr; memoryMenu_index <= memAddr2; memoryMenu_index+=2) {
					printw("x%04X  : %02X %02X\n",memoryMenu_index,the_memoryModulePtr[memoryMenu_index-MEM_START],the_memoryModulePtr[memoryMenu_index+1-MEM_START]);
				}
				printw("\n");
				break;
			case '2' :
				printw("Enter a memory address : \n");
				scanw("%X", &memAddr);
				printw("\n");
				printw("Value : %02X\n\n", memory_fetchByteFrom(the_memoryModulePtr, memAddr, &memoryMenu_error)); 
				break;
			case '3' :
				printw("Enter a memory address and value to store : ");
				scanw("%X %X", &memAddr, &memVal);
				printw("\n");
				memoryMenu_error = memory_storeByteTo(the_memoryModulePtr, memAddr, memVal); 
				break;
			case '4' :
				printw("Enter a memory address : \n");
				scanw("%X", &memAddr);
				printw("\n");
				printw("Value : %04X\n\n", memory_fetchWordFrom(the_memoryModulePtr, memAddr, &memoryMenu_error)); 
				break;
			case '5' :
				printw("Enter a memory address and a value to store : ");
				scanw("%X %X", &memAddr, &memVal);
				printw("\n");
				memoryMenu_error = memory_storeWordTo(the_memoryModulePtr, memAddr, memVal); 
				break;
			case '6' :
				erase();
				break;
			default :
				break;
		}
		if(memoryMenu_error)
			printw("Memory Error! : %s \n\n", main_errorMessage(memoryMenu_error)); 
	}
}


/*
Returns the respective error message given an error code.

parameter (the_error_code): the error code.

return value: an error message as a string.
*/
char* main_errorMessage(int the_error_code) {
	switch (the_error_code) {
		case NULL_POINTER_ERROR :
			return "Null pointer error.";
		case ADDRESS_OOB_ERROR :
			return "Address out of bounds of memory.";
		case INVALID_REG_NUM :
			return "Invalid register number.";
		case INVALID_BYTE_NUM :
			return "Invalid byte number.";
		case OUT_OF_MEM_ERROR :
			return "Error obtaining memory from the heap.";
		default :
			return "Unknown error code.";
	}
}


/* End Program */
